import Promise from 'es6-promise';
import BBCode from "./bbcode"
import Mustche from "mustache"

var events = require('events');

var blank_image = "/image/blank.jpg";

String.prototype.startWith = function (str) {
    var reg = new RegExp("^" + str);
    return reg.test(this);
}

function promise_resolve(val) {
    return new Promise(function (resolve, reject) {
        resolve(val);
    });
}

function drawFieldBorder(drawer, options) {
    var color = options.borderColor;
    if (color == null) {
        color = 'gray';
    }
    if (options.option !== 'readonly') {
        drawer.drawRect(color, options.left, options.top, options.width, options.height);
    }
    return 0;
}

function rbg2hex(rgb) {
    var parts = rgb.split(',');
    return "rgb(" + parts[0] + "," + parts[1] + "," + parts[2] + ")";
}

var Card = class {
    constructor(options) {
        this.options = options;
        this.areas = [];
        for (let i = 0; i < options.length; i++) {
            this.add(new Area(options[i], this));
        }
        this.maxproductid = this.getMaxProductId();
        this.event = new events.EventEmitter();
    }

    add(area) {
        this.areas.push(area);
    }

    selectProductids() {
        let keys = Object.keys(this.data);
        var ids = [];
        for (var key of keys) {
            if (key.startWith("product")) {
                ids.push(this.data[key].id);
            }
        }
        return ids;
    }

    getProductSize() {
        let count = 0;
        let keys = Object.keys(this.data);
        for (var key of keys) {
            if (key.startWith("product")) {
                count++;
            }
        }
        return count;
    }

    getMaxProductId() {
        var result = 0;
        for (let area of this.areas) {
            var id = area.getMaxProductId();
            if (id > result) {
                result = id;
            }
        }
        return result;
    }

    draw(drawer) {
        var promise = promise_resolve(0);
        for (let area of this.areas) {
            promise = promise.then(() => {
                return area.draw(drawer);
            });
        }
        return promise.then(() => {
            return drawer.finish();
        });
    }

    wizard(index) {
        let wizardarea = null;
        for (let area of this.areas) {
            let _area = area.wizard(index);
            if (_area != null) {
                if (wizardarea == null) {
                    wizardarea = _area;
                } else {
                    if (_area.index < wizardarea.index) {
                        wizardarea = _area;
                    }
                }
            }
        }
        return wizardarea;
    }

    applyData(data) {
        this.data = data;
        for (let area of this.areas) {
            area.applyData(data);
        }
    }

    on(event, cb) {
        this.event.on(event, cb);
    }

    onTap(x, y) {
        for (let area of this.areas) {
            if (y >= area.options.top && y < area.options.top + area.options.height) {
                area.onTap(x, y);
            }
        }
    }

    fire(event, detail) {
        this.event.emit(event, detail);
    }

    onLongTap(x, y) {
        for (let area of this.areas) {
            if (y >= area.options.top && y < area.options.top + area.options.height) {
                area.onLongTap(x, y);
            }
        }
    }

    getDropables() {
        var result = [];
        for (let area of this.areas) {
            var dropables = result.concat(area.getDropables());
            for (let dropable of dropables) {
                result.push(dropable);
            }
        }
        return result;
    }
};

var Area = class {
    constructor(options, parent) {
        this.options = options;
        this.children = [];
        this.parent = parent;
        if (options.texts != null) {
            for (let i = 0; i < options.texts.length; i++) {
                this.add(new Text(options.texts[i], this));
            }
        }
        if (options.images != null) {
            for (let i = 0; i < options.images.length; i++) {
                this.add(new Image(options.images[i], this));
            }
        }
        if (options.products != null) {
            for (let i = 0; i < options.products.length; i++) {
                this.add(new Product(options.products[i], this));
            }
        }
    }

    add(element) {
        this.children.push(element);
    }

    draw(drawer) {
        var promise = promise_resolve(0);
        promise = promise.then(() => {
            return drawer.drawImage(this.options.background, 0, this.options.top, this.options.width, this.options.height);
        });
        promise = promise.then(() => {
            drawer.translate(0, this.options.top);
        });
        for (let child of this.children) {
            promise = promise.then(() => {
                return drawFieldBorder(drawer, child.options);
            }).then(() => {
                return child.draw(drawer);
            });
        }
        return promise.finally(() => {
            drawer.restore();
        });
    }

    wizard(index) {
        let wizardarea = null;
        for (let child of this.children) {
            let option = child.options;
            if (option.wizardindex <= index) {
                continue;
            }
            if (option.option === 'readonly') {
                continue;
            }
            if (child.wizardable != null) {
                if (!child.wizardable()) {
                    continue;
                }
            }
            let _area = {
                left: option.left,
                top: option.top + this.options.top,
                width: option.width,
                height: option.height,
                index: option.wizardindex,
                tips: child.getWizardTips()
            };
            if (wizardarea == null) {
                wizardarea = _area;
            } else {
                if (_area.index < wizardarea.index) {
                    wizardarea = _area;
                }
            }
        }
        return wizardarea;
    }

    applyData(data) {
        this.data = data;
        for (let child of this.children) {
            if (child.applyData != null) {
                child.applyData(data);
            } else {
                if (data[child.options.field] != null) {
                    child.data = data[child.options.field];
                } else {
                    if (child.data != null) {
                        delete child.data;
                    }
                }
            }
        }
    }

    onTap(x, y) {
        y = y - this.options.top;
        for (let child of this.children) {
            if (y < child.options.top || y >= child.options.top + child.options.height) {
                continue;
            }
            if (x < child.options.left || x >= child.options.left + child.options.width) {
                continue;
            }
            child.onTap(x, y);
        }
    }

    getLeft() {
        return this.options.left;
    }

    getTop() {
        return this.options.top;
    }

    onLongTap(x, y) {
        y = y - this.options.top;
        for (let child of this.children) {
            if (y < child.options.top || y >= child.options.top + child.options.height) {
                continue;
            }
            if (x < child.options.left || x >= child.options.left + child.options.width) {
                continue;
            }
            if (child.onLongTap != null) {
                child.onLongTap(x, y + this.options.top);
            }
        }
    }

    fire(event, detail) {
        this.parent.fire(event, detail);
    }

    getMaxProductId() {
        var result = 0;
        for (let child of this.children) {
            if (child.options.field.startWith("product")) {
                var id = parseInt(child.options.field.substr(7));
                child.productid = id;
                if (id > result) {
                    result = id;
                }
            }
        }
        return result;
    }

    getDropables() {
        var result = [];
        for (let child of this.children) {
            if (child.getDropable != null) {
                let dropable = child.getDropable();
                if (dropable != null) {
                    result.push(dropable);
                }
            }
        }
        return result;
    }
};

var Text = class {
    constructor(options, parent) {
        this.options = options;
        this.parent = parent;
    }

    getWizardTips() {
        if (this.options.wizardtip != null) {
            return this.options.wizardindex + "." + this.options.wizardtip;
        } else {
            return this.options.wizardindex + ".点击编辑" + this.options.tooltip;
        }
    }

    applyData(data) {
        if (data != null) {
            var template = data[this.options.field];
            if (template != null) {
                this.data = Mustche.render(template, data);
            } else {
                if (this.data != null) {
                    delete this.data;
                }
            }
        } else {
            if (this.data != null) {
                delete this.data;
            }
        }
    }

    draw(drawer) {
        var that = this;
        var text = this.options;
        var value = this.data;
        if (value != null && value.length > 0) {
            drawer.setFont(text.font);
            if (text.color) {
                drawer.setFontColor(rbg2hex(text.color));
            }
            if (text.multiline) {
                let lines = value.split('\n');
                let base = text.top;
                let linebreak = text.font;
                if (text.linebreak != null) {
                    linebreak = text.linebreak;
                }
                let totalheight = lines.length * text.font + linebreak * (lines.length - 1);
                if (totalheight < text.height) {
                    if (text.valign === 'bottom') {
                        base = base + text.height - totalheight;
                    }
                    if (text.valign === 'middle') {
                        base = base + (text.height - totalheight) / 2;
                    }
                }
                for (let i in lines) {
                    var line = lines[i];
                    if (base + text.font > text.top + text.height) {
                        break;
                    }
                    let m = drawer.measureText(line);
                    let left = text.left;
                    if (text.align === "right") {
                        left = left + Math.max(text.width - m.width, 0);
                    }
                    if (text.align === "center") {
                        left = left + Math.max((text.width - m.width) / 2, 0);
                    }
                    drawer.drawText(line, left, base, text.width);
                    base = base + linebreak + text.font;
                }
            } else {
                let m = drawer.measureText(value);
                let left = text.left;
                if (text.align === "right") {
                    left = left + Math.max(text.width - m.width, 0);
                }
                if (text.align === "center") {
                    left = left + Math.max((text.width - m.width) / 2, 0);
                }
                drawer.drawText(value, left, text.top, text.width);
            }
        }
    }

    onTap(x, y) {
        this.parent.fire('taptext', this.options);
    }
};

var Image = class {
    constructor(options, parent) {
        this.options = options;
        this.parent = parent;
    }

    getWizardTips() {
        if (this.options.wizardtip != null) {
            return this.options.wizardindex + "." + this.options.wizardtip;
        } else {
            return this.options.wizardindex + ".点击上传" + this.options.tooltip;
        }
    }

    draw(drawer) {
        if (this.data != null) {
            return drawer.drawImage(this.data, this.options.left, this.options.top, this.options.width, this.options.height);
        }
    }

    onTap(x, y) {
        this.parent.fire('tapimage', this.options);
    }
};

var Product = class {
    constructor(options, parent) {
        this.options = options;
        this.parent = parent;
        this.children = [];
        if (options.texts != null) {
            for (let i = 0; i < options.texts.length; i++) {
                this.add(new Text(options.texts[i], this));
            }
        }
        if (options.images != null) {
            for (let i = 0; i < options.images.length; i++) {
                this.add(new Image(options.images[i], this));
            }
        }
    }

    getWizardTips() {
        if (this.options.wizardtip != null) {
            return this.options.wizardindex + "." + this.options.wizardtip;
        } else {
            return this.options.wizardindex + ".点击选择产品";
        }
    }

    add(element) {
        this.children.push(element);
    }

    draw(drawer) {
        if (this.data != null) {
            var promise = promise_resolve(0);
            promise = promise.then(() => {
                drawer.translate(this.options.left, this.options.top);
            });
            for (let child of this.children) {
                promise = promise.then(() => {
                    return child.draw(drawer);
                });
            }
            return promise.finally(() => {
                drawer.restore();
            });
        }
    }

    onTap(x, y) {
        if (this.data != null) {
            return;
        }
        this.parent.fire('tapaddproduct', this.productid);
    }

    wizardable() {
        return this.data == null;
    }

    getDropable() {
        if (this.data == null) {
            return null;
        }
        return {
            left: this.options.left,
            top: this.options.top + this.parent.getTop(),
            width: this.options.width,
            height: this.options.height,
            over: false,
            cb: (x, y, target) => {
                let drag = target.field;
                let drop = this.options.field;
                this.parent.fire('swapproduct', {product1: drag, product2: drop});
            }
        }
    }

    applyData(data) {
        var data = data[this.options.field];
        if (data != null) {
            this.data = data;
            for (let child of this.children) {
                if (child.applyData != null) {
                    child.applyData(data);
                } else {
                    if (data[child.options.field] != null) {
                        child.data = data[child.options.field];
                    } else {
                        if (child.data != null) {
                            delete child.data;
                        }
                    }
                }
            }
        } else {
            if (this.data != null) {
                delete this.data;
            }
        }
    }

    onLongTap(x, y) {
        if (this.data != null) {
            this.parent.fire('startdrag',
                {
                    target: this,
                    touch: {x: x, y: y},
                    rect: {
                        left: this.options.left,
                        top: this.options.top + this.parent.getTop(),
                        width: this.options.width,
                        height: this.options.height
                    }
                });
        }
    }
};

var Drawer = class {
    constructor(ctx, listener) {
        this.ctx = ctx;
        this.image_buffer = [];
        this.listener = listener;
    }

    finish() {
        return new Promise((resolve, reject) => {
            this.ctx.draw(false, () => {
                if (this.listener != null && this.listener.onFinish != null) {
                    this.listener.onFinish()
                }
                resolve();
            });
        });
    }

    forareaDirect(left, top, cb) {
        this.ctx.save();
        this.ctx.translate(left, top);
        cb();
        this.ctx.restore();
    }

    setFont(size) {
        this.ctx.font = size + "px cursive";
        this.ctx.setTextBaseline("top");
    }

    setFontColor(color) {
        this.ctx.setFillStyle(color);
    }

    translate(left, top) {
        this.ctx.save();
        this.ctx.translate(left, top);
    }

    restore() {
        this.ctx.restore();
    }

    drawRect(color, left, top, width, height) {
        var that = this;
        that.ctx.setStrokeStyle(color);
        that.ctx.setLineWidth(1);
        that.ctx.beginPath();
        that.ctx.moveTo(left, top);
        that.ctx.lineTo(left + width, top);
        that.ctx.lineTo(left + width, top + height);
        that.ctx.lineTo(left, top + height);
        that.ctx.lineTo(left, top);
        that.ctx.stroke();
    }

    measureText(text) {
        var _total = 0;
        var that = this;
        var handle = {
            onText: function (_text) {
                _total = _total + that.ctx.measureText(_text).width;
            }
        };
        new BBCode(handle).parse(text);
        return {width: _total};
    }

    drawText(text, left, top, width) {
        var _left = 0;
        var _state = 0;
        var that = this;
        var handle = {
            onCodeStart: function (name, value) {
                if (name === 'color') {
                    _state = _state + 1;
                    that.ctx.save();
                    that.ctx.setFillStyle(value);
                }
            },
            onCodeEnd: function () {
                if (_state > 0) {
                    _state = _state - 1;
                    that.ctx.restore();
                }
            },
            onText: function (_text) {
                if (_left >= width) {
                    return;
                }
                var measure = that.ctx.measureText(_text);
                that.ctx.fillText(_text, left + _left, top, width - _left);
                _left = _left + measure.width;
            }
        };
        new BBCode(handle).parse(text);
        for (var i = 0; i < _state; i++) {
            that.ctx.restore();
        }
    }

    drawImage(src, left, top, width, height) {
        var that = this;
        if (src.startWith("http://tmp/") || src.startWith("wxfile://") || src.startWith("/")) {
            return that.ctx.drawImage(src, left, top, width, height);
        } else {
            if (that.image_buffer[src] != null) {
                return that.ctx.drawImage(that.image_buffer[src], left, top, width, height);
            } else {
                return new Promise((resolve, reject) => {
                    wx.downloadFile({
                        url: src,
                        success(res) {
                            var tempFilePath = res.tempFilePath;
                            wx.getImageInfo({
                                src: tempFilePath,
                                success(res) {
                                    resolve(tempFilePath);
                                },
                                fail(res) {
                                    reject();
                                }
                            });
                        },
                        fail(res) {
                            reject();
                        }
                    });
                }).then((filepath) => {
                    return filepath;
                }, () => {
                    return blank_image;
                }).then((filepath) => {
                    that.image_buffer[src] = filepath;
                    return that.ctx.drawImage(filepath, left, top, width, height);
                });
            }
        }
    }
}

module.exports = {Card: Card, Drawer: Drawer};